/*
<Copyright (C) 2011 Dru Moore>

This file is part of demorecorder.

vlc-remote is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

demorecorder is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with demorecorder.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "recordpipeline.h"
#include "utilities.h"
#include <QDateTime>
#include <QDebug>

RecordPipeline::RecordPipeline(QObject *parent) :
    QObject(parent)
{
}

RecordPipeline::RecordPipeline(QString name, QString location, GstElement *src, GstCaps *caps, QString encoder_element)
    : QObject()
{
    Q_UNUSED(name);
    //this->bouncing = false;
    //this->name = name;
    this->location = location;
    this->caps = caps;
    this->src = src;
    this->encoder_element = encoder_element;
    this->is_bouncing = false;
    //set the tag handling delegate
    //this.handle_tags_delegate = this.handle_tags;
    //set some default values
    this->start_time = 0;
    //define the queries
    duration_query = gst_query_new_duration(GST_FORMAT_TIME);
    position_query = gst_query_new_position(GST_FORMAT_TIME);
    this->construct_pipeline();
}

RecordPipeline::~RecordPipeline() {
    qDebug() << "In ~RecordPipeline, calling teardown";
    this->teardown_pipeline();
}

bool RecordPipeline::set_src(GstElement *src) {
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_NULL);
    if (NULL != this->src) {
      clear_src();
    }
    this->src = src;
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
    gst_element_link(GST_ELEMENT(this->src), GST_ELEMENT(this->queue));
    return true;
}

void RecordPipeline::clear_src() {
    if (NULL != this->src) {
        gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_NULL);
        gst_element_unlink(GST_ELEMENT(this->src), GST_ELEMENT(this->queue));
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
        this->src = NULL;
    }
}

bool RecordPipeline::set_location(QString location) {
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_NULL);
    this->location = location;
    Utilities::setGObjectProperty(G_OBJECT(this->filesink), "location", location);
    //GValue loc = {0, };
    //g_value_init (&loc, G_TYPE_STRING);
    //g_value_set_string(&loc, location.toLatin1());
    //g_object_set_property(G_OBJECT(this->filesink), "location", &loc);
    return true;
}

QString RecordPipeline::get_location() {
    return this->location;
}

void RecordPipeline::get_duration_info() {
    //qDebug() << "get_duration recorder called. bouncing? " << ((this->is_bouncing) ? "yes" : "no");
    if (this->is_bouncing) {
        bool duration_result,position_result;
        duration_result = gst_element_query(GST_ELEMENT(this->pipeline), this->duration_query);
        position_result = gst_element_query(GST_ELEMENT(this->pipeline), this->position_query);
        if ( duration_result && position_result  ) {
            gint64 pos;
            gint64 dur;
            gst_query_parse_duration(this->duration_query, NULL, &dur);
            gst_query_parse_position(this->position_query, NULL, &pos);
            this->duration =(long long) dur;
            this->position =(long long) pos;
            emit this->position_duration(this->position, this->duration);
            if (this->position >= this->duration) {
                emit this->end_of_stream();
            }
        }
    }
    else {
        emit this->position_duration((get_current_time() - start_time) * 1000000, 0);
    }
}

long long RecordPipeline::get_current_time() {
    QDateTime setTime = QDateTime::fromString (QString("1970-07-18T14:15:09"), Qt::ISODate);
    QDateTime current = QDateTime::currentDateTime();
    return setTime.time().msecsTo(current.time());
}

void RecordPipeline::record() {
    this->start_time = get_current_time();
    qDebug("about to record");
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_PLAYING);
    qDebug("ended about to record");
}

void RecordPipeline::stop() {
    qDebug("about to stop");
    gst_element_set_state(GST_ELEMENT(this->pipeline), GST_STATE_READY);
    qDebug("stopped");
}

void RecordPipeline::teardown_pipeline() {
    qDebug() << "In teardown_pipeline";
    if (NULL != this->pipeline) {
        // NOTE reverse order to construction
        qDebug() << "In teardown_pipeline, UNLINKING";
        //qDebug() << "In teardown_pipeline, encoder";
        gst_element_unlink(GST_ELEMENT(this->encoder),  GST_ELEMENT(this->filesink));
        //qDebug() << "In teardown_pipeline, resampler";
        gst_element_unlink(GST_ELEMENT(this->resampler),  GST_ELEMENT(this->encoder));
        //qDebug() << "In teardown_pipeline, converter";
        gst_element_unlink(GST_ELEMENT(this->converter),  GST_ELEMENT(this->resampler));
        //qDebug() << "In teardown_pipeline, queue";
        gst_element_unlink(GST_ELEMENT(this->queue),  GST_ELEMENT(this->converter));
        //qDebug() << "In teardown_pipeline, src";
        gst_element_unlink(GST_ELEMENT(this->src),  GST_ELEMENT(this->queue));

        qDebug() << "In teardown_pipeline, REMOVING";
        //qDebug() << "In teardown_pipeline, filesink";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->filesink));
        //qDebug() << "In teardown_pipeline, encoder";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->encoder));
        //qDebug() << "In teardown_pipeline, resampler";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->resampler));
        //qDebug() << "In teardown_pipeline, converter";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->converter));
        //qDebug() << "In teardown_pipeline, queue";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->queue));
        //qDebug() << "In teardown_pipeline, src";
        gst_bin_remove(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
        qDebug() << "In teardown_pipeline, SETTING NULL";
        this->filesink = NULL;
        this->encoder = NULL;
        this->resampler = NULL;
        this->queue = NULL;
        this->converter = NULL;
        this->pipeline = NULL;
    }
}

void RecordPipeline::construct_pipeline() {
    qDebug() << "In construct_pipeline, CREATING ELEMENTS";
    this->pipeline = GST_PIPELINE(gst_pipeline_new("pipeline"));

    if (NULL != this->src) {
        //qDebug() << "In construct_pipeline, adding src";
        gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->src));
    }

    //qDebug() << "In construct_pipeline, getting converter";
    this->converter = gst_element_factory_make("audioconvert", "converter");
    g_assert(this->converter);
    //qDebug() << "In construct_pipeline, adding converter";
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->converter));

    //qDebug() << "In construct_pipeline, getting queue";
    this->queue = gst_element_factory_make("queue", "buffer");
    g_assert(this->queue);
    //qDebug() << "In construct_pipeline, adding queue";
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->queue));

    //qDebug() << "In construct_pipeline, getting resampler";
    this->resampler = gst_element_factory_make("audioresample", "resampler");
    g_assert(this->resampler);
    Utilities::setGObjectProperty(G_OBJECT(this->resampler), "quality", 10.0);
    //GValue qua = {0, };
    //g_value_init (&qua, G_TYPE_DOUBLE);
    //g_value_set_double(&qua, 10);
    //g_object_set_property(G_OBJECT(this->resampler), "quality", &qua);
    //qDebug() << "In construct_pipeline, adding resampler";
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->resampler));

    //qDebug() << "In construct_pipeline, getting encoder";
    this->encoder = gst_element_factory_make(this->encoder_element.toLatin1(), "encoder");
    g_assert(this->encoder);
    //qDebug() << "In construct_pipeline, adding encoder";
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->encoder));


    //qDebug() << "In construct_pipeline, getting filesink";
    this->filesink = gst_element_factory_make("filesink", "fileoutput");
    g_assert(this->filesink);
    Utilities::setGObjectProperty(G_OBJECT(this->filesink), "location", location);
    /*GValue loc = {0, };
    g_value_init (&loc, G_TYPE_STRING);
    g_value_set_string(&loc, location.toLatin1());
    g_object_set_property(G_OBJECT(this->filesink), "location", &loc);*/
    //qDebug() << "In construct_pipeline, adding filesink";
    gst_bin_add(GST_BIN(this->pipeline), GST_ELEMENT(this->filesink));

    qDebug() << "In construct_pipeline, LINKING";
    if (NULL != this->src && gst_element_link(GST_ELEMENT(this->src), GST_ELEMENT(this->queue))) {

    }

    if (gst_element_link(GST_ELEMENT(this->queue), GST_ELEMENT(this->converter))) {

    }

    if (gst_element_link(GST_ELEMENT(this->converter), GST_ELEMENT(this->resampler))) {

    }

    if (gst_element_link_filtered(GST_ELEMENT(this->resampler), GST_ELEMENT(this->encoder), GST_CAPS(this->caps))) {

    }

    if (gst_element_link(GST_ELEMENT(this->encoder), GST_ELEMENT(this->filesink))) {

    }

}
